package com.itxixi.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

/**
 * Redis 客户端，使用 Redisson <a href="https://github.com/redisson/redisson">Redisson</a>
 *
 * @author Fuzhengwei bugstack.cn @小傅哥
 */
@Configuration
@EnableConfigurationProperties(RedisClientConfigProperties.class)
public class RedisClientConfig {

    @Bean("redissonClient")
    public RedissonClient redissonClient(ConfigurableApplicationContext applicationContext, RedisClientConfigProperties properties) {
        Config config = new Config();
        // 根据需要可以设定编解码器；https://github.com/redisson/redisson/wiki/4.-%E6%95%B0%E6%8D%AE%E5%BA%8F%E5%88%97%E5%8C%96
//         config.setCodec(new RedisCodec());
         config.setCodec(JsonJacksonCodec.INSTANCE);

        //创建config对象来配置redis客户端的各种参数  配置redission里的redis配置
        config.useSingleServer()
                .setAddress("redis://" + properties.getHost() + ":" + properties.getPort())
//                .setPassword(properties.getPassword())  %没有密码 不用设置
                .setConnectionPoolSize(properties.getPoolSize())
                .setConnectionMinimumIdleSize(properties.getMinIdleSize())
                .setIdleConnectionTimeout(properties.getIdleTimeout())
                .setConnectTimeout(properties.getConnectTimeout())
                .setRetryAttempts(properties.getRetryAttempts())
                .setRetryInterval(properties.getRetryInterval())
                .setPingConnectionInterval(properties.getPingInterval())
                .setKeepAlive(properties.isKeepAlive())
        ;

        return Redisson.create(config);
    }

    static class RedisCodec extends BaseCodec {

        private final Encoder encoder = in -> {
            ByteBuf out = ByteBufAllocator.DEFAULT.buffer();// 提供的缓冲区类，用于高效处理字节数组和 IV/O 操作。
            try {                       //基于 ByteBuf 的输出流，提供字节写入功能，类似于  ByteArrayOutputStream
                ByteBufOutputStream os = new ByteBufOutputStream(out);
                //这里使用了 JsoN 序列化框架(如 FastUSON） 来将in 对象写成SON字符串，然后通过os 输出流写入到 ByteBuf中。
                // SerializerFeature,writeClasName选项确保序列化时写入类的名称，便于反序列化时识别类型
                JSON.writeJSONString(os, in, SerializerFeature.WriteClassName);
                return os.buffer(); //返回的是缓冲区 ByteBuf ，它包合了序列化后的数据。
            } catch (IOException e) {
                out.release();
                throw e;
            } catch (Exception e) {
                out.release();
                throw new IOException(e);
            }
            // Lambda 表达式中抛出受检异常（如 IOException）需要在 Lambda 体内捕获并处理。由于
            //Lambda 表达式不能直接抛出受检异常，所以这里用了 try-catch 块进行捕获，
            // 并通过 throw new IOException(e）抛出封装后的异常。
        };
        //使用json框架将从流中读取的数据反序列化为一个 object 实例。
        // 因为 ByteBuf 里面是 JSON 格式的字符串，所以可以通过 parseobject 方法将其解析回对象。
        private final Decoder<Object> decoder = (buf, state) -> JSON.parseObject(new ByteBufInputStream(buf), Object.class);

        @Override
        public Decoder<Object> getValueDecoder() {
            return decoder;
        }

        @Override
        public Encoder getValueEncoder() {
            return encoder;
        }

    }

}
